#ifndef TASKQUEUE_H
#define TASKQUEUE_H

#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>

using std::unique_lock;
using std::queue;
using std::mutex;
using std::condition_variable;
using std::function;

class TaskQueue {
private:
    using ElemType = function<void()>;

    size_t _queueSize;
    queue<ElemType> _queue;
    mutex _mutex;
    condition_variable _notFull;
    condition_variable _notEmpty;
    bool _flag;

public:
    TaskQueue(size_t queueSize) :
            _queueSize(queueSize), _queue(), _mutex(),
            _notFull(), _notEmpty(),
            _flag(true){

    }

    ~TaskQueue(){}

    void push(ElemType value){
        unique_lock<mutex> uniqueLock(_mutex);
        while (full()) {
            _notFull.wait(uniqueLock);
        }
        _queue.push(value);

        _notEmpty.notify_one();
    }

    ElemType pop(){
        unique_lock<mutex> uniqueLock(_mutex);
        while (empty() && _flag) {
            _notEmpty.wait(uniqueLock);
        }
        if(!_flag){
            return nullptr;
        }


        ElemType tmp = _queue.front();
        _queue.pop();

        _notFull.notify_one();
        return tmp;
    }

    bool full() const{
        return _queueSize == _queue.size();
    }

    bool empty() const{
        return 0 == _queue.size();
    }

    void wakeup(){
        _flag = false;
        _notEmpty.notify_all();
    }
};

#endif //TASKQUEUE_H
